﻿
using System;
using System.IO;
using System.Collections;

namespace CCompiler {
    public class Scanner {
        /* current character */
        private char ch;

        /* input buffer index */
        private int buffc;

        /*  input buufer */
        private string inbuff;

        /* cuurent line */
        public int curLine;

        private ArrayList errorsList;

        public Scanner(string input, ArrayList errorsList)//, SymboleTable symTable)
        {
            buffc = 0;
            inbuff = input;
            curLine = 1;
            //this.symtab = symTable;

            this.errorsList = errorsList;
            /* read the first char */
            GetNextChar();
        }

        private void Error(string msg) {
            errorsList.Add("{" + curLine + "}: " + "Lexical error: " + msg);
        }

        private void GetNextChar() {
            if (buffc >= inbuff.Length)// || buffc < 0)
                ch = (char)0;
            else
                ch = inbuff[buffc++];

            if (ch == '\n')
                ++curLine;
        }

        public Token GetNextToken(SymbolTable symtab) {
            bool isComment;
            string lexBuff;
            Token token = null;

        begin:
            isComment = false;
            lexBuff = "";

            /* skip white spaces */
            while (Char.IsWhiteSpace(ch))
                GetNextChar();

            /* identifier or keyword */
            if (Char.IsLetter(ch) || ch == '_') {
                do {
                    lexBuff += ch;
                    GetNextChar();
                } while (Char.IsDigit(ch) || Char.IsLetter(ch) || ch == '_');

                TokenCode code;
                Symbol sym = symtab.Lookup(lexBuff);
                if (sym == null)
                    code = TokenCode.Id;
                else
                    code = sym.code;

                token = new Token(code, lexBuff);
                return token;
            }

            /* number */
            if (Char.IsDigit(ch)) {
                do {
                    lexBuff += ch;
                    GetNextChar();
                } while (Char.IsDigit(ch));

                token = new Token(TokenCode.Num, lexBuff, Int32.Parse(lexBuff));
                return token;
            }

            /* char or string token */
            if (ch == '\'' || ch == '"') {
                char terminator = ch;

                GetNextChar();
                while (ch != terminator) {
                    lexBuff += ch;
                    GetNextChar();
                }
                GetNextChar();

                lexBuff = lexBuff.Replace("\\n", "\n");
                lexBuff = lexBuff.Replace("\\r", "\r");
                lexBuff = lexBuff.Replace("\\t", "\t");

                if (terminator == '\'' && lexBuff.Length > 1)
                    Error("more than one character.");

                if (terminator == '\'')
                    /* char */
                    token = new Token(TokenCode.Num, lexBuff[0].ToString(), (int)lexBuff[0]);
                else
                    /* string */
                    token = new Token(TokenCode.String, lexBuff, lexBuff.Length);

                return token;
            }

            lexBuff += ch;
            GetNextChar();
            if (ch != '\0')
                lexBuff += ch;

            int tokType = GetTokenType(lexBuff);
            switch (tokType) {
                case -1:
                    /* comments */
                    isComment = true;
                    /* one line comment (skip characters until end of line */
                    if (lexBuff == "//")
                        while (ch != (char)0 && ch != '\n')
                            GetNextChar();
                    /* multi line comment */
                    else if (lexBuff == "/*") {
                        do {
                            GetNextChar();
                            lexBuff += ch;
                        } while (ch != (char)0 && lexBuff.Length > 2 && lexBuff.Substring(lexBuff.Length - 2, 2) != "*/");
                    }
                    if (isComment) {
                        GetNextChar();
                        goto begin;
                    }
                    break;
                case 1:
                    /* one char token */
                    if (lexBuff.Length > 1)
                        --buffc;
                    GetNextChar();
                    return new Token((TokenCode)((int)lexBuff[0]), lexBuff[0].ToString());
                default:
                    /* two char token */
                    GetNextChar();
                    return new Token((TokenCode)tokType, lexBuff);
            }

            /* anything else ( Null char token ) */
            token = new Token(0, ch.ToString());
            GetNextChar();
            return token;
        }

        private int GetTokenType(string lexbuff) {
            int retVal = -1;

            switch (lexbuff) {
                case "<=": retVal = 400; break;
                case ">=": retVal = 401; break;
                case "==": retVal = 402; break;
                case "!=": retVal = 403; break;
                case "||": retVal = 404; break;
                case "&&": retVal = 405; break;
                case "++": retVal = 406; break;
                case "--": retVal = 407; break;
                case "//":
                case "/*": retVal = -1; break;
                /* null char */
                case "": retVal = 0; break;
                /* on char token */
                default: retVal = 1; break;
            }
            return retVal;
        }
    }
}